package com.whfc.mach.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.whfc.base.param.AppFileExportParam;
import com.whfc.base.service.AppExportService;
import com.whfc.common.exception.BizException;
import com.whfc.common.file.FileHandler;
import com.whfc.common.file.FilePathConfig;
import com.whfc.common.file.properties.FileExpirationRules;
import com.whfc.common.result.PageData;
import com.whfc.common.result.ResultEnum;
import com.whfc.common.util.DateUtil;
import com.whfc.common.util.FileUtil;
import com.whfc.common.util.PageUtil;
import com.whfc.common.util.RandomUtil;
import com.whfc.fuum.service.SysDeptService;
import com.whfc.mach.dao.*;
import com.whfc.mach.dto.MachDictDataDTO;
import com.whfc.mach.dto.maintain.MaintainDTO;
import com.whfc.mach.dto.maintain.MaintainItemDTO;
import com.whfc.mach.dto.maintain.MaintainPayDTO;
import com.whfc.mach.entity.AppMachMaintain;
import com.whfc.mach.enums.MachDict;
import com.whfc.mach.param.maintain.MachMaintainAddParam;
import com.whfc.mach.param.maintain.MachMaintainEditParam;
import com.whfc.mach.service.AppMachMaintainService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.*;

/**
 * @author sun_guodong
 * @date 2021-08-12
 */
@DubboService(interfaceClass = AppMachMaintainService.class, version = "1.0.0")
public class AppMachMaintainServiceImpl implements AppMachMaintainService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private AppMachMaintainMapper appMachMaintainMapper;

    @Autowired
    private AppMachMaintainPayMapper appMachMaintainPayMapper;

    @Autowired
    private AppMachMaintainItemMapper appMachMaintainItemMapper;

    @Autowired
    private AppMachMaintainFileMapper appMachMaintainFileMapper;

    @Autowired
    private AppMachDictMapper appMachDictMapper;

    @Autowired
    private FilePathConfig filePathConfig;

    @Autowired
    private FileHandler fileHandler;

    @DubboReference(interfaceClass = AppExportService.class, version = "1.0.0")
    private AppExportService appExportService;

    @DubboReference(interfaceClass = SysDeptService.class, version = "1.0.0")
    private SysDeptService sysDeptService;

    private static final String FILE_DESC = "设备保养记录";

    private static final String SUFFIX = "xls";

    @Override
    public PageData<MaintainDTO> list(Integer deptId, Integer pageNum, Integer pageSize, Date startDate, Date endDate, String keyword, Integer machType) throws BizException {
        logger.info("设备保养记录列表服务,deptId:{},pageNum:{},pageSize:{},startDate:{},endDate:{},keyword:{},machType:{}",
                deptId, pageNum, pageSize, startDate, endDate, keyword, machType);
        PageHelper.startPage(pageNum, pageSize);
        List<MaintainDTO> list = appMachMaintainMapper.selectByDeptId(deptId, startDate, endDate, keyword, machType);
        PageHelper.clearPage();
        if (list.size() == 0) {
            return PageUtil.pageData(PageInfo.of(list));
        }
        this.handleData(list);
        return PageUtil.pageData(PageInfo.of(list));
    }

    @Override
    public PageData<MaintainDTO> listByMachId(Integer machId, Integer pageNum, Integer pageSize) throws BizException {
        logger.info("设备保养记录列表(单台)服务,machId:{},pageNum:{},pageSize:{}", machId, pageNum, pageSize);
        PageHelper.startPage(pageNum, pageSize);
        List<MaintainDTO> list = appMachMaintainMapper.selectByMachId(machId);
        PageHelper.clearPage();
        if (list.size() == 0) {
            return PageUtil.pageData(PageInfo.of(list));
        }
        this.handleData(list);
        return PageUtil.pageData(PageInfo.of(list));
    }

    @Override
    public MaintainDTO detail(Integer maintainId) throws BizException {
        logger.info("设备保养详情服务,maintainId:{}", maintainId);
        MaintainDTO data = appMachMaintainMapper.selectById(maintainId);
        if (data == null) {
            throw new BizException(ResultEnum.PARAM_ERROR.getCode(), "该保养记录不存在");
        }
        // 查找保养项目
        List<MaintainItemDTO> itemList = appMachMaintainItemMapper.selectByMaintainId(maintainId);
        // 查找保养费用
        List<MaintainPayDTO> payList = appMachMaintainPayMapper.selectByMaintainId(maintainId);
        Double payTotal = 0D;
        for (MaintainPayDTO maintainPayDTO : payList) {
            payTotal += maintainPayDTO.getPayValue();
        }
        // 查询保养文件
        List<String> fileList = appMachMaintainFileMapper.selectByMaintainId(maintainId);
        data.setPayTotal(payTotal);
        data.setPayList(payList);
        data.setItemList(itemList);
        data.setFileList(fileList);
        return data;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(MachMaintainAddParam param) throws BizException {
        logger.info("添加保养记录服务,param:{}", param.toString());
        Integer deptId = param.getDeptId();
        // 插入保养记录
        AppMachMaintain maintainRecord = new AppMachMaintain();
        maintainRecord.setDeptId(deptId);
        maintainRecord.setMaintainRemark(param.getMaintainRemark());
        maintainRecord.setMaintainDate(param.getMaintainDate());
        maintainRecord.setMaintainUserId(param.getMaintainUserId());
        maintainRecord.setMaintainUserName(param.getMaintainUserName());
        maintainRecord.setMachId(param.getMachId());
        maintainRecord.setMachCode(param.getMachCode());
        maintainRecord.setMachTypeName(param.getMachTypeName());
        maintainRecord.setWorkHour(param.getWorkHour());
        maintainRecord.setWorkMile(param.getWorkMile());
        appMachMaintainMapper.insertSelective(maintainRecord);
        Integer maintainId = maintainRecord.getId();

        // 插入保养文件
        List<String> fileList = param.getFileList();
        appMachMaintainFileMapper.batchInsert(maintainId, fileList);

        // 插入保养项目
        List<MaintainItemDTO> itemList = param.getItemList();
        appMachMaintainItemMapper.batchInsert(maintainId, itemList);

        // 插入保养费用
        List<MaintainPayDTO> payList = param.getPayList();
        appMachMaintainPayMapper.batchInsert(deptId, maintainId, param.getMachId(), param.getMachCode(), param.getMachTypeName(), param.getMaintainDate(), payList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(MachMaintainEditParam param) throws BizException {
        logger.info("编辑保养记录服务,param:{}", param.toString());
        Integer maintainId = param.getMaintainId();
        // 更新保养记录
        AppMachMaintain maintainRecord = new AppMachMaintain();
        maintainRecord.setId(maintainId);
        maintainRecord.setMaintainRemark(param.getMaintainRemark());
        maintainRecord.setMaintainDate(param.getMaintainDate());
        maintainRecord.setMachId(param.getMachId());
        maintainRecord.setMachCode(param.getMachCode());
        maintainRecord.setMachTypeName(param.getMachTypeName());
        maintainRecord.setWorkHour(param.getWorkHour());
        maintainRecord.setWorkMile(param.getWorkMile());
        appMachMaintainMapper.updateByPrimaryKeySelective(maintainRecord);

        // 插入保养文件
        appMachMaintainFileMapper.deleteLogic(maintainId);
        List<String> fileList = param.getFileList();
        appMachMaintainFileMapper.batchInsert(maintainId, fileList);

        // 插入保养项目
        appMachMaintainItemMapper.deleteLogic(maintainId);
        List<MaintainItemDTO> itemList = param.getItemList();
        appMachMaintainItemMapper.batchInsert(maintainId, itemList);

        // 插入保养费用
        appMachMaintainPayMapper.deleteLogic(maintainId);
        List<MaintainPayDTO> payList = param.getPayList();
        appMachMaintainPayMapper.batchInsert(param.getDeptId(), maintainId, param.getMachId(), param.getMachCode(), param.getMachTypeName(), param.getMaintainDate(), payList);
    }

    @Override
    public void del(Integer maintainId) throws BizException {
        logger.info("删除保养记录服务,maintainId:{}", maintainId);
        // 删除保养记录
        appMachMaintainMapper.deleteLogic(maintainId);

        // 删除保养文件
        appMachMaintainFileMapper.deleteLogic(maintainId);

        // 删除保养项目
        appMachMaintainItemMapper.deleteLogic(maintainId);

        // 删除保养费用
        appMachMaintainPayMapper.deleteLogic(maintainId);
    }

    @Override
    public void export(Integer deptId, Date startDate, Date endDate, String keyword, Integer machType) throws BizException {
        logger.info("导出保养记录服务,deptId:{},startDate:{},endDate:{},keyword:{}",
                deptId, startDate, endDate, keyword);
        String fileName = RandomUtil.getRandomFileName();
        Integer fileId = this.install(deptId, fileName, SUFFIX);
        // 获取数据
        List<MaintainDTO> list = appMachMaintainMapper.selectByDeptId(deptId, startDate, endDate, keyword, machType);
        this.handleData(list);
        try {
            // 获取模板
            ClassPathResource resource = new ClassPathResource("templates/machMaintainTemplate.xls");
            InputStream templateFileInputStream = resource.getInputStream();
            File tempFile = FileUtil.copyTemplateFile(templateFileInputStream);

            // 写数据
            FileInputStream fileInputStream = new FileInputStream(tempFile);
            HSSFWorkbook workbook = new HSSFWorkbook(fileInputStream);
            HSSFSheet sheet = workbook.getSheetAt(0);

            // 填充数据
            this.fillData(deptId, startDate, endDate, workbook, sheet, list);

            // 保存excel
            FileOutputStream fos = new FileOutputStream(tempFile);
            workbook.write(fos);
            fos.flush();
            fos.close();

            // 上传oss
            String name = filePathConfig.getFilePath("mach/maintain/record", fileName, SUFFIX);
            FileInputStream inputStream = new FileInputStream(tempFile);
            String upload = fileHandler.upload(name, inputStream, FileExpirationRules.DAYS_1);
            // 更新文件状态
            AppFileExportParam fileExportParam = new AppFileExportParam();
            fileExportParam.setFileUrl(upload);
            fileExportParam.setFileState(1);
            fileExportParam.setId(fileId);
            appExportService.update(fileExportParam);
        } catch (Exception e) {
            logger.error("导出保养记录失败", e);
        }


    }

    /**
     * 获取数据
     *
     * @param list
     * @return
     */
    private void handleData(List<MaintainDTO> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        List<Integer> maintainIds = new ArrayList<>();
        for (MaintainDTO maintainDTO : list) {
            maintainIds.add(maintainDTO.getMaintainId());
        }

        // 查找保养费用
        List<MaintainPayDTO> payList = appMachMaintainPayMapper.selectByMaintainIds(maintainIds);
        Map<Integer, List<MaintainPayDTO>> payMap = new HashMap<>();
        Map<Integer, Double> payTotalMap = new HashMap<>();
        for (MaintainPayDTO maintainPayDTO : payList) {
            Integer maintainId = maintainPayDTO.getMaintainId();
            List<MaintainPayDTO> maintainPayDTOS = payMap.get(maintainId);
            if (maintainPayDTOS == null) {
                maintainPayDTOS = new ArrayList<>();
                payMap.put(maintainId, maintainPayDTOS);
            }
            maintainPayDTOS.add(maintainPayDTO);

            Double aDouble = payTotalMap.get(maintainId);
            if (aDouble == null) {
                payTotalMap.put(maintainId, maintainPayDTO.getPayValue());
            } else {
                payTotalMap.put(maintainId, aDouble + maintainPayDTO.getPayValue());
            }
        }

        // 查找保养项目
        List<MaintainItemDTO> itemList = appMachMaintainItemMapper.selectByMaintainIds(maintainIds);
        Map<Integer, List<MaintainItemDTO>> itemMap = new HashMap<>();
        for (MaintainItemDTO maintainItemDTO : itemList) {
            Integer maintainId = maintainItemDTO.getMaintainId();
            List<MaintainItemDTO> maintainItemDTOS = itemMap.get(maintainId);
            if (maintainItemDTOS == null) {
                maintainItemDTOS = new ArrayList<>();
                itemMap.put(maintainId, maintainItemDTOS);
            }
            maintainItemDTOS.add(maintainItemDTO);
        }

        for (MaintainDTO maintainDTO : list) {
            Integer maintainId = maintainDTO.getMaintainId();
            maintainDTO.setItemList(itemMap.get(maintainId));
            maintainDTO.setPayList(payMap.get(maintainId));
            maintainDTO.setPayTotal(payTotalMap.get(maintainId));
        }
    }

    /**
     * 往excel中填充数据
     *
     * @param deptId
     * @param startDate
     * @param endDate
     * @param workbook
     * @param sheet
     * @param list
     */
    private void fillData(Integer deptId, Date startDate, Date endDate, HSSFWorkbook workbook, HSSFSheet sheet, List<MaintainDTO> list) {
        // 查找项目名称
        String deptName = sysDeptService.getDeptName(deptId);
        String title = new StringBuffer(deptName)
                .append(" ")
                .append(DateUtil.formatDate(startDate))
                .append("至")
                .append(DateUtil.formatDate(endDate))
                .append(" 设备维修保养记录明细").toString();
        // 设置第一行title
        /********************标题部分  row0***********************/
        HSSFRow row0 = sheet.createRow(0);
        HSSFCell cell00 = row0.createCell(0);
        cell00.setCellValue(title);
        /********************抬头部分  row1***********************/
        HSSFRow row1 = sheet.createRow(1);
        row1.createCell(0).setCellValue("序号");
        row1.createCell(1).setCellValue("设备编号");
        row1.createCell(2).setCellValue("设备类型");
        row1.createCell(3).setCellValue("维修保养项目");
        List<MachDictDataDTO> payList = this.getPayList(deptId);
        int cellIdx = 4;
        for (MachDictDataDTO machDictDataDTO : payList) {
            row1.createCell(cellIdx).setCellValue(machDictDataDTO.getName());
            cellIdx++;
        }
        row1.createCell(cellIdx).setCellValue("费用小计");
        row1.createCell(cellIdx + 1).setCellValue("维修保养说明");
        row1.createCell(cellIdx + 2).setCellValue("维修保养人");
        row1.createCell(cellIdx + 3).setCellValue("维修保养日期");
        row1.createCell(cellIdx + 4).setCellValue("备注");
        // 标题部分设置颜色
        this.setColor(workbook, HSSFColor.HSSFColorPredefined.GREEN.getIndex(), cell00);
        // 标题部分合并
        this.mergedRegion(sheet, 0, 0, 0, cellIdx + 4);
        // 标题部分居中
        this.setCenter(workbook, cell00);

        /********************数据部分  从开始row2***********************/
        // 填充数据
        Map<String, Double> totalPayMap = new HashMap<>(payList.size());
        Double totalPay = 0D;
        int rowIdx = 2;
        for (MaintainDTO maintainDTO : list) {
            HSSFRow row2 = sheet.createRow(rowIdx);
            row2.createCell(0).setCellValue(rowIdx - 1);
            row2.createCell(1).setCellValue(maintainDTO.getMachCode());
            row2.createCell(2).setCellValue(maintainDTO.getMachTypeName());
            row2.createCell(3).setCellValue(this.handleMaintainItem(maintainDTO.getItemList()));
            List<MaintainPayDTO> maintainPayList = maintainDTO.getPayList();
            Map<String, Double> map = new HashMap<>();
            for (MaintainPayDTO maintainPayDTO : maintainPayList) {
                String payName = maintainPayDTO.getPayName();
                Double payValue = maintainPayDTO.getPayValue();
                Double aDouble = map.get(payName);
                if (aDouble == null) {
                    map.put(payName, payValue);
                } else {
                    map.put(payName, aDouble + payValue);
                }

                // 每一项费用统计
                Double aDouble1 = totalPayMap.get(payName);
                if (aDouble1 == null) {
                    totalPayMap.put(payName, payValue);
                } else {
                    totalPayMap.put(payName, aDouble1 + payValue);
                }
                totalPay += payValue;
            }
            // 从第5列开始
            int cellIdx1 = 4;
            for (MachDictDataDTO machDictDataDTO : payList) {
                String name = machDictDataDTO.getName();
                row2.createCell(cellIdx1).setCellValue(map.get(name) == null ? 0D : map.get(name));
                cellIdx1++;
            }
            row2.createCell(cellIdx1).setCellValue(maintainDTO.getPayTotal());
            row2.createCell(cellIdx1 + 1).setCellValue(maintainDTO.getMaintainRemark() == null ? "" : maintainDTO.getMaintainRemark());
            row2.createCell(cellIdx1 + 2).setCellValue(maintainDTO.getMaintainUserName());
            row2.createCell(cellIdx1 + 3).setCellValue(DateUtil.formatDate(maintainDTO.getMaintainDate()));
            rowIdx++;
        }

        /********************总计部分***********************/
        HSSFRow row3 = sheet.createRow(rowIdx);
        HSSFCell cell30 = row3.createCell(0);
        cell30.setCellValue("总计");

        int cellIdx1 = 4;
        for (MachDictDataDTO machDictDataDTO : payList) {
            String name = machDictDataDTO.getName();
            row3.createCell(cellIdx1).setCellValue(totalPayMap.get(name) == null ? 0D : totalPayMap.get(name));
            cellIdx1++;
        }
        row3.createCell(cellIdx1).setCellValue(totalPay);

        // 总计部分合并单元格并居中
        this.mergedRegion(sheet, rowIdx, rowIdx, 0, 3);
        this.setCenter(workbook, cell30);
    }


    /**
     * 合并单元格
     *
     * @param sheet
     * @param firstRow
     * @param lastRow
     * @param firstCol
     * @param lastCol
     */
    private void mergedRegion(HSSFSheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) {
        // 合并单元格
        CellRangeAddress cra = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol);
        sheet.addMergedRegion(cra);
    }

    /**
     * 设置颜色
     *
     * @param workbook
     * @param color
     * @param cell
     */
    private void setColor(HSSFWorkbook workbook, short color, HSSFCell cell) {
        CellStyle cellStyle = workbook.createCellStyle();
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        cellStyle.setFillForegroundColor(color);
        cell.setCellStyle(cellStyle);
    }

    /**
     * 居中
     *
     * @param workbook
     * @param cell
     */
    private void setCenter(HSSFWorkbook workbook, HSSFCell cell) {
        // 居中样式
        HSSFCellStyle centerStyle = workbook.createCellStyle();
        centerStyle.setAlignment(HorizontalAlignment.CENTER);
        cell.setCellStyle(centerStyle);
    }

    /**
     * 处理保养项目
     *
     * @param itemList
     * @return
     */
    private String handleMaintainItem(List<MaintainItemDTO> itemList) {
        StringBuffer stringBuffer = new StringBuffer();
        for (MaintainItemDTO maintainItemDTO : itemList) {
            stringBuffer.append(maintainItemDTO.getItemName()).append(" ");
        }
        return stringBuffer.toString();
    }

    /**
     * 安装要下载的文件
     *
     * @param deptId
     * @param fileName
     * @param suffix
     */
    private Integer install(Integer deptId, String fileName, String suffix) {
        String fileDesc = FILE_DESC;
        AppFileExportParam fileExport = new AppFileExportParam();
        fileExport.setDeptId(deptId);
        fileExport.setFileName(fileName);
        fileExport.setFileFormat(suffix.toUpperCase());
        fileExport.setFileState(0);
        fileExport.setFileDesc(fileDesc);
        Integer id = appExportService.install(fileExport);
        return id;
    }


    /**
     * 获取保养费用类目
     *
     * @param deptId
     * @return
     */
    private List<MachDictDataDTO> getPayList(Integer deptId) {
        Integer pid = appMachDictMapper.selectByCode(MachDict.MAINTAIN_PAY.getCode());
        List<MachDictDataDTO> machPayList = appMachDictMapper.selectByDeptIdAndPid(deptId, pid);
        return machPayList;
    }


}
